1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package com.google.common.hash;
16
17 import static com.google.common.base.Preconditions.checkArgument;
18 import static com.google.common.base.Preconditions.checkNotNull;
19 import static com.google.common.base.Preconditions.checkState;
20
21 import java.io.Serializable;
22 import java.security.MessageDigest;
23 import java.security.NoSuchAlgorithmException;
24 import java.util.Arrays;
25
26
27
28
29
30
31
32 final class MessageDigestHashFunction extends AbstractStreamingHashFunction
33 implements Serializable {
34 private final MessageDigest prototype;
35 private final int bytes;
36 private final boolean supportsClone;
37 private final String toString;
38
39 MessageDigestHashFunction(String algorithmName, String toString) {
40 this.prototype = getMessageDigest(algorithmName);
41 this.bytes = prototype.getDigestLength();
42 this.toString = checkNotNull(toString);
43 this.supportsClone = supportsClone();
44 }
45
46 MessageDigestHashFunction(String algorithmName, int bytes, String toString) {
47 this.toString = checkNotNull(toString);
48 this.prototype = getMessageDigest(algorithmName);
49 int maxLength = prototype.getDigestLength();
50 checkArgument(bytes >= 4 && bytes <= maxLength,
51 "bytes (%s) must be >= 4 and < %s", bytes, maxLength);
52 this.bytes = bytes;
53 this.supportsClone = supportsClone();
54 }
55
56 private boolean supportsClone() {
57 try {
58 prototype.clone();
59 return true;
60 } catch (CloneNotSupportedException e) {
61 return false;
62 }
63 }
64
65 @Override public int bits() {
66 return bytes * Byte.SIZE;
67 }
68
69 @Override public String toString() {
70 return toString;
71 }
72
73 private static MessageDigest getMessageDigest(String algorithmName) {
74 try {
75 return MessageDigest.getInstance(algorithmName);
76 } catch (NoSuchAlgorithmException e) {
77 throw new AssertionError(e);
78 }
79 }
80
81 @Override public Hasher newHasher() {
82 if (supportsClone) {
83 try {
84 return new MessageDigestHasher((MessageDigest) prototype.clone(), bytes);
85 } catch (CloneNotSupportedException e) {
86
87 }
88 }
89 return new MessageDigestHasher(getMessageDigest(prototype.getAlgorithm()), bytes);
90 }
91
92 private static final class SerializedForm implements Serializable {
93 private final String algorithmName;
94 private final int bytes;
95 private final String toString;
96
97 private SerializedForm(String algorithmName, int bytes, String toString) {
98 this.algorithmName = algorithmName;
99 this.bytes = bytes;
100 this.toString = toString;
101 }
102
103 private Object readResolve() {
104 return new MessageDigestHashFunction(algorithmName, bytes, toString);
105 }
106
107 private static final long serialVersionUID = 0;
108 }
109
110 Object writeReplace() {
111 return new SerializedForm(prototype.getAlgorithm(), bytes, toString);
112 }
113
114
115
116
117 private static final class MessageDigestHasher extends AbstractByteHasher {
118
119 private final MessageDigest digest;
120 private final int bytes;
121 private boolean done;
122
123 private MessageDigestHasher(MessageDigest digest, int bytes) {
124 this.digest = digest;
125 this.bytes = bytes;
126 }
127
128 @Override
129 protected void update(byte b) {
130 checkNotDone();
131 digest.update(b);
132 }
133
134 @Override
135 protected void update(byte[] b) {
136 checkNotDone();
137 digest.update(b);
138 }
139
140 @Override
141 protected void update(byte[] b, int off, int len) {
142 checkNotDone();
143 digest.update(b, off, len);
144 }
145
146 private void checkNotDone() {
147 checkState(!done, "Cannot re-use a Hasher after calling hash() on it");
148 }
149
150 @Override
151 public HashCode hash() {
152 checkNotDone();
153 done = true;
154 return (bytes == digest.getDigestLength())
155 ? HashCode.fromBytesNoCopy(digest.digest())
156 : HashCode.fromBytesNoCopy(Arrays.copyOf(digest.digest(), bytes));
157 }
158 }
159 }